namespace SimpleWebServices.Data
{
    using System;
    using System.Collections;
    using System.Collections.Generic;
    using System.Collections.ObjectModel;
    using System.Collections.Specialized;
    using System.Globalization;
    using System.Linq;
    using System.Net;
    using System.Text;
    using System.Web;
    using System.Xml;
    using System.Xml.Schema;
    using System.Xml.Serialization;
    using SimpleWebServices;
    using SimpleWebServices.Web;

    [XmlRoot("data")]
    public sealed class DataCollection : IEnumerable<KeyValuePair<string, string>>, IXmlSerializable, IRecord
    {
        public DataCollection()
        {
            this.Items = new Collection<KeyValuePair<string, string>>();
        }

        public bool Completed
        {
            get;
            set;
        }

        public int Count
        {
            get
            {
                return this.Items.Count;
            }
        }

        public string Entity
        {
            get
            {
                Entity value = new Entity();
                value.Append(this.Completed);
                foreach (var datum in this.Items)
                {
                    value.Append(string.Format(CultureInfo.InvariantCulture, "{0}={1}", datum.Key, datum.Value));
                }

                return value;
            }
        }

        public HttpCacheability? Cacheability
        {
            get;
            set;
        }

        public DateTime? Created
        {
            get;
            set;
        }

        public string Etag
        {
            get;
            set;
        }

        public Expiration Expires
        {
            get;
            set;
        }

        public DateTime? Modified
        {
            get;
            set;
        }

        public HttpStatusCode? Status
        {
            get;
            set;
        }

        public Token? Token
        {
            get;
            set;
        }

        public AbsoluteUri Urn
        {
            get;
            set;
        }

        private Collection<KeyValuePair<string, string>> Items
        {
            get;
            set;
        }

        public KeyValuePair<string, string> this[int index]
        {
            get
            {
                return this.Items[index];
            }

            set
            {
                this.Items[index] = value;
            }
        }

        public string this[string name]
        {
            get
            {
                StringBuilder buffer = null;

                foreach (var datum in this)
                {
                    if (string.Equals(name, datum.Key, StringComparison.OrdinalIgnoreCase))
                    {
                        if (null == buffer)
                        {
                            buffer = new StringBuilder();
                        }

                        if (0 != buffer.Length)
                        {
                            buffer.Append(',');
                        }

                        buffer.Append(datum.Value);
                    }
                }

                return null == buffer ? null as string : buffer.ToString();
            }

            set
            {
                Collection<KeyValuePair<string, string>> removals = new Collection<KeyValuePair<string, string>>();

                foreach (var datum in this.Items)
                {
                    if (string.Equals(name, datum.Key, StringComparison.OrdinalIgnoreCase))
                    {
                        removals.Add(datum);
                    }
                }

                foreach (var removal in removals)
                {
                    this.Items.Remove(removal);
                }

                if (null == value)
                {
                    this.Items.Add(new KeyValuePair<string, string>(name, value));
                }
                else
                {
                    string[] parts = value.Split(new char[] { ',' }, StringSplitOptions.None);
                    foreach (string part in parts)
                    {
                        this.Items.Add(new KeyValuePair<string, string>(name, part));
                    }
                }
            }
        }

        public static bool operator ==(DataCollection obj, DataCollection comparand)
        {
            bool result = false;

            if (object.ReferenceEquals(null, obj))
            {
                result = object.ReferenceEquals(null, comparand);
            }
            else
            {
                result = obj.Equals(comparand);
            }

            return result;
        }

        public static bool operator !=(DataCollection obj, DataCollection comparand)
        {
            bool result = false;

            if (object.ReferenceEquals(null, obj))
            {
                result = !object.ReferenceEquals(null, comparand);
            }
            else
            {
                result = !obj.Equals(comparand);
            }

            return result;
        }

        public static DataCollection FromPostData(NameValueCollection form)
        {
            if (null == form)
            {
                throw new ArgumentNullException("form");
            }

            DataCollection result = new DataCollection();

            for (int i = 0; i < form.Count; i++)
            {
                string value = form[i];
                if (null != value && value.Contains(","))
                {
                    string[] parts = value.Split(new char[] { ',' }, StringSplitOptions.RemoveEmptyEntries);
                    foreach (string part in parts)
                    {
                        result.Add(form.Keys[i], part);
                    }
                }
                else
                {
                    result.Add(form.Keys[i], form[i]);
                }
            }

            return result;
        }

        public void Add(DataCollection data)
        {
            if (null == data)
            {
                throw new ArgumentNullException("data");
            }

            foreach (var datum in data.Items)
            {
                this.Items.Add(datum);
            }
        }

        public void Add(string name, string value)
        {
            if (null == name)
            {
                throw new ArgumentNullException("name");
            }
            else if (0 == name.Length)
            {
                throw new ArgumentOutOfRangeException("name");
            }

            this.Add(new KeyValuePair<string, string>(name, value));
        }

        public void Add(KeyValuePair<string, string> item)
        {
            if (null == item.Value)
            {
                this.Items.Add(new KeyValuePair<string, string>(item.Key, item.Value));
            }
            else
            {
                string[] parts = item.Value.Split(new char[] { ',' }, StringSplitOptions.None);
                foreach (string part in parts)
                {
                    this.Items.Add(new KeyValuePair<string, string>(item.Key, part));
                }
            }
        }

        public bool Contains(string name)
        {
            return 0 != this.Items
                .Where(x => x.Key.Equals(name, StringComparison.OrdinalIgnoreCase))
                .Count();
        }

        public bool Contains(string name, string value)
        {
            return 0 != this.Items
                .Where(x => x.Key.Equals(name, StringComparison.OrdinalIgnoreCase) && x.Value.Equals(value, StringComparison.Ordinal))
                .Count();
        }

        public override bool Equals(object obj)
        {
            bool result = false;

            if (!object.ReferenceEquals(null, obj))
            {
                if (object.ReferenceEquals(this, obj))
                {
                    result = true;
                }
                else
                {
                    DataCollection cast = obj as DataCollection;

                    if (!object.ReferenceEquals(null, cast))
                    {
                        if (this.Items.Count == cast.Items.Count)
                        {
                            result = true;
                            foreach (var datum in this.Items)
                            {
                                if (!cast.Items.Contains(datum))
                                {
                                    result = false;
                                    break;
                                }
                            }
                        }
                    }
                }
            }

            return result;
        }

        public override int GetHashCode()
        {
            return this.ToString().GetHashCode();
        }

        public override string ToString()
        {
            return this.Serialize().CreateNavigator().OuterXml;
        }

        IEnumerator IEnumerable.GetEnumerator()
        {
            return (this.Items as IEnumerable).GetEnumerator();
        }

        IEnumerator<KeyValuePair<string, string>> IEnumerable<KeyValuePair<string, string>>.GetEnumerator()
        {
            return (this.Items as IEnumerable<KeyValuePair<string, string>>).GetEnumerator();
        }

        XmlSchema IXmlSerializable.GetSchema()
        {
            throw new NotSupportedException();
        }

        void IXmlSerializable.ReadXml(XmlReader reader)
        {
            if (null == reader)
            {
                throw new ArgumentNullException("reader");
            }

            string value = reader.GetAttribute("completed");
            if (!string.IsNullOrEmpty(value))
            {
                this.Completed = XmlConvert.ToBoolean(value);
            }

            if (reader.IsEmptyElement)
            {
                reader.Read();
            }
            else
            {
                string name = reader.Name;
                while (reader.Read())
                {
                    if (XmlNodeType.EndElement == reader.NodeType && reader.Name == name)
                    {
                        reader.Read();
                        break;
                    }
                    else if ("value".Equals(reader.Name, StringComparison.OrdinalIgnoreCase))
                    {
                        this.Add(reader.GetAttribute("name"), reader.ReadString());
                    }
                }
            }
        }

        void IXmlSerializable.WriteXml(XmlWriter writer)
        {
            if (null == writer)
            {
                throw new ArgumentNullException("writer");
            }

            if (this.Completed)
            {
                writer.WriteAttributeString("completed", "true");
            }

            foreach (var datum in this.Items)
            {
                writer.WriteStartElement("value");
                writer.WriteAttributeString("name", datum.Key);
                writer.WriteString(datum.Value);
                writer.WriteEndElement();
            }
        }
    }
}